#include <windows.h>
#include <commctrl.h>
#include <strsafe.h>
#include <unordered_map>

class CTinyButton
{
public:
    CTinyButton()
    {
        //调试窗口 使用WriteConsole()输出日志
        // InitTerminal();
        m_IsBtDown = FALSE;
        m_IsRng = FALSE;
        m_nWidth = 100;
        m_nHeight = 50;
        m_BkColor = RGB(225, 225, 225);
        m_TxtColor = RGB(0, 0, 0);

        m_hFont = CreateFont(20, 0, 0, 0, FW_NORMAL, FALSE, FALSE, 0, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, TEXT("Consolas"));
    }
    ~CTinyButton()
    {
    }
    void InitTerminal()
    {
        AllocConsole();
        SetConsoleTitle(TEXT("CTinyButton"));
        hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
    }

public:
    //调试
    HANDLE hOutput;
    //字体
    HFONT m_hFont;
    //按钮名称
    LPCTSTR m_lpClssName;
    //是否按下
    BOOL m_IsBtDown;
    //是否圆角
    BOOL m_IsRng;
    //按钮id
    UINT m_ID;
    //圆角数值
    UINT m_rectRgn;
    // x轴
    LONG m_nCx;
    // y轴
    LONG m_nCy;
    //按钮宽
    LONG m_nWidth;
    //按钮高
    LONG m_nHeight;
    COLORREF m_BkColor;
    COLORREF m_TxtColor;
    //鼠标拖拽
    // BOOL dragMove;
    //鼠标经过时的背景色
    COLORREF m_hoverBkColor;
    //监控鼠标离开
    BOOL m_bMouseTracking;
    //鼠标事件
    std::unordered_map<UINT, void (*)()> m_pfnEvent;

public:
    BOOL SetText(const LPCTSTR lpClssName)
    {
        m_lpClssName = lpClssName;
        return TRUE;
    }
    BOOL SetPosition(LONG nCx, LONG nCy)
    {
        m_nCx = nCx;
        m_nCy = nCy;
        return TRUE;
    }
    BOOL Attach(HWND hParent, HMENU uID = 0)
    {
        HWND hBtnWnd = CreateWindow(WC_BUTTON, m_lpClssName, WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
                                    m_nCx, m_nCy, m_nWidth, m_nHeight,
                                    hParent, uID, NULL, NULL);
        if (hBtnWnd == NULL)
        {
            return FALSE;
        }

        SetWindowLongPtr(hBtnWnd, GWLP_WNDPROC, (LONG_PTR)lpButtonProc);
        SetWindowLong(hBtnWnd, GWLP_USERDATA, (LONG_PTR)this);
        return TRUE;
    }
    static LRESULT CALLBACK lpButtonProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        CTinyButton *pThis = (CTinyButton *)GetWindowLongPtr(hWnd, GWLP_USERDATA);

        switch (uMsg)
        {
        case WM_PAINT:
            // PAINTSTRUCT ps;
            // BeginPaint(hWnd, &ps);
            // TODO: Add any drawing code here...
            pThis->OnPaint(hWnd, uMsg, wParam, lParam);
            // EndPaint(hWnd, &ps);
            break;
        case WM_ERASEBKGND:
            return TRUE;
        case WM_DRAWITEM:
        {
        }
        break;
        case WM_COMMAND:
        {
        }
        break;
        case WM_LBUTTONDOWN:
        {
            pThis->OnLButtonDown(hWnd, uMsg, wParam, lParam);
        }
        break;
        case WM_LBUTTONUP:
        {
            pThis->OnLButtonUp(hWnd, uMsg, wParam, lParam);
        }
        break;
        case WM_MOUSEMOVE:
        {
            pThis->OnMouseMove(hWnd, uMsg, wParam, lParam);
        }
        break;
        case WM_MOUSEHOVER:
        {
            pThis->OnMouseHover(hWnd, uMsg, wParam, lParam);
        }
        break;
        case WM_MOUSELEAVE:
        {
            pThis->OnMouseLeave(hWnd, uMsg, wParam, lParam);
        }
        break;
        default:
        {
        }
        }
        return DefWindowProc(hWnd, uMsg, wParam, lParam);
    }
    void SetIsDown(BOOL bol)
    {
        m_IsBtDown = bol;
    }
    //设置是否为圆角按钮
    void SetRectRgn(BOOL isRng, UINT rectRgn = 50)
    {
        m_IsRng = isRng;
        m_rectRgn = rectRgn;
    }
    void SetSize(int nWidth, int nHeight)
    {
        m_nWidth = nWidth;
        m_nHeight = nHeight;
    }
    //设置按钮背景色
    void SetBkColor(COLORREF cor)
    {
        m_BkColor = cor;
    }
    //设置文本颜色
    void SetTxtColor(COLORREF cor)
    {
        m_TxtColor = cor;
    }
    COLORREF SwapBkColor(COLORREF cor)
    {
        COLORREF tmp = m_BkColor;
        m_BkColor = cor;
        return tmp;
    }

public:
    virtual LRESULT OnCreate(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        ExecEvent(uMsg);
        return LRESULT(0);
    }
    virtual LRESULT OnPaint(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        ExecEvent(uMsg);
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);
        //创建新画笔
        HPEN hNewPen = CreatePen(PS_SOLID, 2, RGB(180, 180, 180));
        //将新画笔选入DC中
        HPEN hOldPen = (HPEN)SelectObject(hdc, hNewPen);
        //设置文字背景为透明模式
        SetBkMode(hdc, TRANSPARENT);
        //设置文本前景色(即文本颜色),m_TxtColr为成员变量,由外界传入
        SetTextColor(hdc, m_TxtColor);

        //获得按钮窗口大小
        RECT rect;
        GetClientRect(hWnd, &rect);
        //获得按钮窗口上的文本
        TCHAR tchar[128] = {0};
        GetWindowText(hWnd, tchar, 128);

        //按钮的两种样式,按下与弹起
        if (m_IsBtDown == FALSE) //弹起样式
        {
            //获取按钮背景颜色,
            COLORREF color = m_BkColor;
            HBRUSH hBrush1 = CreateSolidBrush(color);
            HBRUSH hOldBrush = (HBRUSH)SelectObject(hdc, hBrush1);

            //画按钮边框,弹起样式为下边和左边的边框能看见,另两边就移到窗口外了
            //判断是否是圆角按钮,
            if (m_IsRng)
            {
                //是就画圆角矩形
                RoundRect(hdc, -2, -2, rect.right - rect.left - 2, rect.bottom - rect.top - 2, m_rectRgn, m_rectRgn);
            }
            else
            {
                //不是就画矩形
                Rectangle(hdc, -2, -2, rect.right - rect.left, rect.bottom - rect.top);
            }
            HFONT hOldFont = (HFONT)SelectObject(hdc, m_hFont);
            DrawText(hdc, tchar, lstrlen(tchar), &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
            SelectObject(hdc, hOldFont);

            SelectObject(hdc, hOldBrush);
            DeleteObject(hBrush1);
        }
        else
        {
            //创建以背景色+5的画刷
            COLORREF color = RGB(GetGValue(m_BkColor) - 30, GetGValue(m_BkColor) - 30, GetBValue(m_BkColor) - 30);
            HBRUSH hBrush1 = CreateSolidBrush(color);
            HBRUSH hOldBrush = (HBRUSH)SelectObject(hdc, hBrush1);

            //绘制边框,按下样式为上边和右边为看得见的边框,另两边一样也移动窗口外
            //判断是否是圆角形状
            if (m_IsRng)
            {
                //画圆角矩形
                RoundRect(hdc, 1, 1, rect.right - rect.left + 2, rect.bottom - rect.top + 2, m_rectRgn, m_rectRgn);
            }
            else
            {
                //不是就画直角矩形
                Rectangle(hdc, 1, 1, rect.right - rect.left + 2, rect.bottom - rect.top + 2);
            }
            //文字矩形区域偏移,(这是向下偏移2个单位)
            OffsetRect(&rect, 0, 2);
            //绘制文本
            HFONT hOldFont = (HFONT)SelectObject(hdc, m_hFont);
            DrawText(hdc, tchar, lstrlen(tchar), &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
            SelectObject(hdc, hOldFont);

            //释放画笔,画刷等资源
            SelectObject(hdc, hOldBrush);
            DeleteObject(hBrush1);
        }
        //释放画笔,画刷等资源　
        SelectObject(hdc, hOldPen);
        DeleteObject(hNewPen);
        EndPaint(hWnd, &ps);
        return LRESULT(0);
    }
    virtual LRESULT OnLButtonDown(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        if (!m_IsBtDown)
        {
            SetIsDown(TRUE);
            ExecEvent(uMsg);

            RECT rcWnd = {};
            GetClientRect(hWnd, &rcWnd);
            InvalidateRect(hWnd, &rcWnd, TRUE);
        }
        return LRESULT(0);
    }
    virtual LRESULT OnLButtonUp(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        if (m_IsBtDown)
        {
            SetIsDown(FALSE);
            ExecEvent(uMsg);

            RECT rcWnd = {};
            GetClientRect(hWnd, &rcWnd);
            InvalidateRect(hWnd, &rcWnd, TRUE);
        }
        return LRESULT(0);
    }
    virtual LRESULT OnMouseMove(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        //鼠标移动过程中不停执行
        ExecEvent(uMsg);
        //鼠标移动过程中执行一次(改变按钮颜色)
        if (!m_bMouseTracking)
        {
            TRACKMOUSEEVENT tme;
            tme.cbSize = sizeof(tme);
            tme.dwFlags = TME_LEAVE | TME_HOVER;
            tme.dwHoverTime = 1;
            tme.hwndTrack = hWnd;
            TrackMouseEvent(&tme);
            m_bMouseTracking = TRUE; //避免重复执行
        }
        return LRESULT(0);
    }
    virtual LRESULT OnMouseHover(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        if (m_bMouseTracking)
        {
            ExecEvent(uMsg);
            m_bMouseTracking = TRUE;

            m_hoverBkColor = SwapBkColor(RGB(GetGValue(m_BkColor) - 10, GetGValue(m_BkColor) - 10, GetBValue(m_BkColor) - 10));
            RECT rcWnd = {};
            GetClientRect(hWnd, &rcWnd);
            InvalidateRect(hWnd, &rcWnd, TRUE);
        }
        return LRESULT(0);
    }
    virtual LRESULT OnMouseLeave(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        if (m_bMouseTracking)
        {
            ExecEvent(uMsg);
            m_bMouseTracking = FALSE;

            SetIsDown(FALSE);
            SwapBkColor(m_hoverBkColor);
            RECT rcWnd = {};
            GetClientRect(hWnd, &rcWnd);
            InvalidateRect(hWnd, &rcWnd, TRUE);
        }
        return LRESULT(0);
    }
    virtual LRESULT AddEvent(UINT uMsg, void (*pfnEvent)())
    {
        m_pfnEvent.emplace(uMsg, pfnEvent);
        return LRESULT(0);
    }
    virtual LRESULT ExecEvent(UINT uMsg)
    {
        if (m_pfnEvent.find(uMsg) != m_pfnEvent.end())
        {
            m_pfnEvent[uMsg]();
        }
        return LRESULT(0);
    }
};
